home *** CD-ROM | disk | FTP | other *** search
- /*
- File: A-Law compressor.c
-
- Contains: Sample sound compression component to convert
- 16-bit linear samples to ALaw-encoded data
-
- Written by: Mark Cookson
-
- Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 8/13/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
- #include "ComponentDispatch.h"
- #include <Memory.h>
- #include <Errors.h>
- #include <SoundInput.h>
- #include <Components.h>
- #include <Gestalt.h>
-
- #include <Sound.h>
- #include <SoundComponents.h>
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Standard Compression Component Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* ==============================================================================
- SetSource
-
- This routine sets the component we should call to get more data. We must remember
- this component and also tell it the data format of our component requires.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source)
- {
- #pragma unused (sourceID)
-
- globals->sourceComponent = source; // remember our source component
- globals->sourceDataPtr = nil; // nothing read from source yet
-
- return (noErr);
- }
-
- /* ==============================================================================
- SetOutput
-
- This routine sets the data format our component should output. If we can't output
- the requested format, we should return a pointer to the format we do support,
- and return an error, and the Sound Manager will attempt the conversion for us.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual)
- {
- globals->outputSamples = requested->sampleCount; // no. samples to output
- if (globals->outputSamples > kMaxOutputSamples) { // too much for our buffer
- globals->outputSamples = kMaxOutputSamples; // only output what we can
- }
-
- // make sure data format and sample sizes match
- if (requested->format == kOutputSampleFormat) { // formats match
- globals->thisComponent = *requested;
- return (noErr); // no problem outputting this format
- } else {
- // If we can't output the requested format, the Sound Manager will make an attempt to convert our
- // format into something that can be used. In order for the Sound Manager to do this, we need to
- // tell it here the format we will be outputting, so it can setup the proper conversion. This is really
- // handy if your algorithm only outputs 16-bit data, but the Sound Manager is requesting 8-bit. In this
- // case, the Sound Manager will automatically convert your 16-bit data to 8-bit.
-
- *actual = &globals->thisComponent; // tell the Sound Manager what we will output
- return (paramErr); // force the Sound Manager to convert for us
- }
- }
-
- /* ==============================================================================
- GetSourceData
-
- This routine is called when the Sound Manager wants your component to compress
- some more data. It should first make sure it has some source data, then
- compress into an internal buffer and return that buffer to the Sound Manager.
- If the Sound Manager is requesting the data in reverse, you must compress
- the data starting from the end of the source buffer, and the Sound Manager will
- reverse the samples for you.
-
- NOTE: This will most often be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr)
- {
- SoundComponentDataPtr sourceDataPtr;
- unsigned long samplesConverted, framesToConvert, bytesToSkip;
- ComponentResult result;
- short *inputBuffer;
-
- result = noErr;
- sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component
-
- if (sourceDataPtr == nil) // source buffer was flushed
- {
- result = PrimeSource(globals); // start with all new source data
- }
- else if (sourceDataPtr->sampleCount == 0) // source buffer is empty
- {
- result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr); // fill it up // continue where we left off
- }
-
- if (result != noErr) // bail if error
- return (result);
-
- sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component
-
- if ((sourceDataPtr->format == globals->thisComponent.format) || // input and output are same
- (sourceDataPtr->buffer == nil)) // or no source buffer
- {
- globals->sourceDataPtr = nil; // get new source next time
- *resultDataPtr = sourceDataPtr; // pass source on down
- }
- else
- {
- // convert the source samples into frames
- framesToConvert = sourceDataPtr->sampleCount / globals->compInfo.samplesPerPacket;
-
- if (framesToConvert) // source has some data for us
- {
- if (framesToConvert > globals->outputFrames)
- framesToConvert = globals->outputFrames; // limited to size of output
-
- // convert frames back into samples to quantize the source correctly
- samplesConverted = framesToConvert * globals->compInfo.samplesPerPacket;
-
- inputBuffer = (short *)sourceDataPtr->buffer; // point at input buffer
-
- bytesToSkip = (samplesConverted / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame;
- sourceDataPtr->buffer += bytesToSkip; // skip over the source consumed
-
- sourceDataPtr->sampleCount -= samplesConverted; // this many samples will be compressed
-
- // Do the compression
- CompressALaw(inputBuffer, globals->buffer, framesToConvert, sourceDataPtr->numChannels);
- }
- else
- samplesConverted = 0; // no samples were converted
-
- globals->thisComponent.buffer = (Byte *) globals->buffer; // data in this buffer
- globals->thisComponent.sampleCount = samplesConverted; // return num. samples converted
-
- *resultDataPtr = &globals->thisComponent; // return description of compressed data
- }
-
- return (result);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine is used to prime the source. It gets the first load of data
- // from the source and intializes the output format and compression factors.
-
- ComponentResult PrimeSource(SoundComponentGlobalsPtr globals)
- {
- ComponentResult result;
- SoundComponentDataPtr sourceDataPtr;
-
- // get data from source
- result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
- if (result != noErr)
- return (result);
- if (globals->sourceDataPtr == nil)
- return (paramErr);
-
- sourceDataPtr = globals->sourceDataPtr;
- globals->thisComponent.flags = sourceDataPtr->flags; // copy flags unchanged
- globals->thisComponent.sampleRate = sourceDataPtr->sampleRate; // copy sample rate unchanged
- globals->thisComponent.numChannels = sourceDataPtr->numChannels; // copy numchannels unchanged
-
- globals->compInfo.recordSize = sizeof(CompressionInfo);
- result = GetCompressionInfo(fixedCompression, sourceDataPtr->format,
- sourceDataPtr->numChannels, sourceDataPtr->sampleSize,
- &globals->compInfo);
-
- globals->destCompInfo.recordSize = sizeof(CompressionInfo);
- globals->destCompInfo.format = globals->thisComponent.format;
- GetCompressorInfo(&globals->destCompInfo); // get compression info
- globals->destCompInfo.bytesPerFrame = globals->destCompInfo.bytesPerPacket * sourceDataPtr->numChannels;
-
- globals->outputFrames = globals->outputSamples / globals->compInfo.samplesPerPacket;
-
- return (result);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Compressor-specific Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine initializes the compressor.
-
- void InitializeCompressor(SoundComponentGlobalsPtr globals) // initialize our compressor state
- {
- #pragma unused (globals)
-
- // Here you should initialize any state used by your compression alorithm (such as predictors)
- // to default values. This routine will be called whenever a new sound is started so you can set
- // up correctly. In our example, we do not have any state, so we have nothing to set up.
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine returns information about the compression ratios.
-
- void GetCompressorInfo(CompressionInfoPtr cp)
- {
- if (cp->recordSize > sizeof(CompressionInfo)) // limit amount we return
- cp->recordSize = sizeof(CompressionInfo);
-
- cp->compressionID = fixedCompression; // must set this to fixedCompression
- cp->samplesPerPacket = 1; // no. samples in one compressed packet
- cp->bytesPerPacket = 1; // no. bytes in a packet
- cp->bytesPerSample = 2; // no. bytes in a compressed sample
- }
-
- //Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems"
- inline short Normalize (short *value) {
- int numShiftBits, msb, nextmsb;
-
- numShiftBits = 0;
- msb = (*value & 0x8000) >> 15;
- nextmsb = (*value & 0x4000) >> 14;
- while (msb == nextmsb) {
- *value <<= 1;
- numShiftBits++;
- msb = (*value & 0x8000) >> 15;
- nextmsb = (*value & 0x4000) >> 14;
- }
- return (numShiftBits);
- }
-
- //Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems"
- void CompressALaw(short *inbuf, Byte *outbuf, unsigned long framesToConvert,
- unsigned long numChannels)
- {
- int i;
- short origSample, X, YYY, ZZZZ, numberOfShiftBits;
- unsigned char newSample;
-
- for (i = 0; i < framesToConvert*numChannels; i++) { // loop over all source frames
- origSample = *inbuf; // get ALaw sample
- inbuf ++;
-
- if (origSample == -32768) { //check boundary condition
- newSample = 0x2A;
- } else {
- X = (origSample & 0x8000) >> 8; //create the sign bit
- if (origSample & 0x8000) //abs(origSample)
- origSample = -origSample;
- if (origSample < 0x0100) { //check for zero segment
- ZZZZ = (origSample >> 4) & 0x000F; //if zero segment, shift down 4 to get position
- YYY = 0x0000; //create segment
- } else { //not zero segment
- numberOfShiftBits = Normalize (&origSample);
- ZZZZ = (origSample & 0x3C00) >> 10; //create position
- YYY = (7 - numberOfShiftBits) << 4; //create segment
- }
- newSample = X | YYY | ZZZZ; //combine sign bit, segment, and position
- newSample ^= 0xD5; //invert necessary bits
- }
-
- *outbuf = newSample; // output 16-bit sample
- outbuf ++;
- }
- }
-
-